﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpBolt.Basics;
using SharpBolt.Time;
using System.ComponentModel;
using InductBase;
using SharpBolt.Graphics;
using SharpBolt.Graphics.GraphicSources;
using Newtonsoft.Json;

namespace SharpBolt.Graphics.GraphicInstances
{
    [TypeConverter(typeof(ExpandableObjectConverter))]
    [JsonObject]
    public class SpriteInstance : GraphicInstance
    {
        [JsonIgnore]
        public SpriteSource SpriteSource { get { return Source as SpriteSource; } set { Source = value; } }

        [JsonIgnore]
        public SbBitmap CurrentBitmap { get; private set; }

        public event EventHandler OnEnd;
        public event EventHandler OnStopPoint;
        public event EventHandler FrameChanged;

        [DefaultValue(false)]
        public bool IsAnimationEnded { get; set; }

        public enum PlayOptions { Shot, Loop, PingPong }

        [DefaultValue(PlayOptions.Loop)]
        public PlayOptions Options { get; set; }

        [JsonIgnore]
        public SpriteSource.Sequence CurrentSequence { get; private set; }
        [JsonIgnore]
        public SpriteSource.Frame CurrentFrame { get; private set; }

        public int CurrentSequenceIndex
        {
            get
            {
                if (CurrentSequence == null) return -1;
                return SpriteSource.Sequences.IndexOf(CurrentSequence);
            }

            set
            {
                int sequenceIndex = value;
                int frameIndex = 0;
                CurrentSequence = SpriteSource.Sequences[sequenceIndex];
                CurrentFrame = CurrentSequence.Frames[frameIndex];
                _CurrentFrameIndex = frameIndex;
            }
        }

        public int CurrentFrameIndex
        {
            get
            {
                if (CurrentSequence == null) return -1;
                if (CurrentFrame == null) return -1;
                return _CurrentFrameIndex;
            }

            set
            {
                if (CurrentSequence == null) return;
                if (CurrentFrame == null) return;
                _CurrentFrameIndex = value;
                Actualise();
            }
        }

        [JsonIgnore]
        private Timer _timer { get; set; }

        private int _currentFrameIndex;
        private int _CurrentFrameIndex
        {
            get { return _currentFrameIndex; }
            set
            {
                if (_currentFrameIndex != value)
                {
                    _currentFrameIndex = value;

                    if (FrameChanged != null)
                        FrameChanged(this, EventArgs.Empty);
                }
            }
        }

        public SpriteInstance(SpriteSource source)
            : base()
        {
            _timer = Engine.TimeManager.CreateTimer();
            _timer.Ended += new EventHandler(Timer_Ended);
            Transformation = new Transformation(Vec2.Null, new Vec2(1, 1), Vec2.Null, 0);
            Color = Colors.White;
            Options = PlayOptions.Loop;
            Direction = 1;
            Speed = 1;
            IsAnimationEnded = false;
            this.Source = source;
            HotSpot = source.HotSpot;

            PlaySequence();
        }

        void Timer_Ended(object sender, EventArgs e)
        {
            NextFrame();
        }



        [DefaultValue(1)]
        public int Direction { get; set; }

        double speed = 1;
        public double Speed
        {
            get { return speed; }
            set
            {
                if (value < 0) Direction = -1;
                if (value == 0) Direction = 0;
                if (value > 0) Direction = 1;
                speed = Math.Abs(value);
            }
        }

        private void NextFrame()
        {
            int nextIndex = _CurrentFrameIndex + Math.Sign(Direction);
            if (Options == PlayOptions.Shot)
            {
                if (nextIndex < CurrentSequence.Frames.Count && nextIndex >= 0)
                {
                    _CurrentFrameIndex = nextIndex;
                    IsAnimationEnded = false;
                }
                else
                {
                    if (!IsAnimationEnded)
                    {
                        IsAnimationEnded = true;
                        if (OnEnd != null) OnEnd(this, EventArgs.Empty);
                    }
                }
            }

            if (Options == PlayOptions.Loop)
            {
                if (Direction > 0)
                {
                    if (nextIndex < CurrentSequence.Frames.Count)
                        _CurrentFrameIndex = nextIndex;
                    else
                    {
                        _CurrentFrameIndex = 0;
                        if (OnStopPoint != null) OnStopPoint(this, EventArgs.Empty);
                    }
                }
                else
                {
                    if (nextIndex >= 0)
                        _CurrentFrameIndex = nextIndex;
                    else
                    {
                        _CurrentFrameIndex = CurrentSequence.Frames.Count - 1;
                        if (OnStopPoint != null) OnStopPoint(this, EventArgs.Empty);
                    }
                }

            }

            if (Options == PlayOptions.PingPong)
            {
                if (Direction > 0)
                {
                    if (nextIndex < CurrentSequence.Frames.Count)
                        _CurrentFrameIndex = nextIndex;
                    else
                    {
                        Direction = -Direction;
                        if (OnStopPoint != null) OnStopPoint(this, EventArgs.Empty);
                    }
                }
                else
                {
                    if (nextIndex >= 0)
                        _CurrentFrameIndex = nextIndex;
                    else
                    {
                        Direction = -Direction;
                        if (OnStopPoint != null) OnStopPoint(this, EventArgs.Empty);
                    }
                }
            }

            Actualise();
        }

        private void Actualise()
        {
            CurrentFrame = CurrentSequence.Frames[_CurrentFrameIndex];
            CurrentBitmap = CurrentFrame.Bitmap;

            if (speed > 0)
            {

                _timer.Start((long)(CurrentFrame.Milliseconds / speed));
            }
        }

        public void IfNoPlaySequence(string sequenceName = "", int frameIndex = 0, double speed = 1)
        {
            if (CurrentSequence.Name != sequenceName)
                PlaySequence(sequenceName, frameIndex, speed);
        }

        public void PlaySequence(string sequenceName, int frameIndex = 0, double speed = 1)
        {
            int sequenceIndex = SpriteSource.GetSequenceIndex(sequenceName);

            if (sequenceIndex < 0)
                throw new ArgumentException(sequenceName);

            PlaySequence(sequenceIndex, frameIndex, speed);
        }


        public void PlaySequence(int sequenceIndex = 0, int frameIndex = 0, double speed = 1)
        {
            CurrentSequence = SpriteSource.Sequences[sequenceIndex];
            CurrentFrame = CurrentSequence.Frames[frameIndex];
            _CurrentFrameIndex = frameIndex;

            Speed = speed;
            IsAnimationEnded = false;

            Actualise();
        }

        public override void Draw(Painter painter)
        {
            if (Hidden) return;

            var trans = Transformation.Copy();

            trans.Position += HotSpot * trans.Scale;
            trans.Shift += CurrentFrame.Shift - HotSpot * trans.Scale;

            if (CurrentBitmap != null)
                painter.DrawBitmap(CurrentBitmap, trans, Color);
        }

        public override InductBase.Vec2 Size
        {
            get
            {
                return this.CurrentFrame.Bitmap.Size;
            }
        }

        public override void Destroy()
        {
            _timer.Drop();
        }
 
    }
}
